from view.endSrc.ModeFilter import ModeFilter
import numpy as np
from scipy import stats
from view.endSrc.tHighDensityFilter import tHighDensityFilter

class KdeFilter(ModeFilter):
    '''
    This class filters out high density points (actually distance values) through KDE
    '''
    def __init__(self, distances: np.array, topPercent: np.float, filterTable: tHighDensityFilter):
        '''

        :param distances:
        :param topPercent:
        :param filterTable: used to write this filter outputing ids into DB
        '''
        ModeFilter.__init__(self, filterTable)

        self.topPercent = topPercent
        self.distances = distances

    def __call__(self, ids: list):
        '''
        Returns the index(filterIdx) corresponding to the filtered high density point(distances)

        Paremeters
        ----------
        ids: Index of the distances that will be filtered

        Returns
        ----------
        filterIdx: the index corresponding to the filtered high density point(distances)

        Examples
        --------
        filter = KdeFilter(distances. topPercent)
        filterIds = filter(ids)
        '''
        disComp = np.asarray(self.distances)[ids]  # Fetch the distances corresponding to ids

        if np.unique(disComp).size < 2:
            return None

        kde = stats.gaussian_kde(disComp)  # construct kernel density function
        densityComp = kde(disComp)  # get the density corresponding to the distance of ids

        # choose the topPercent of higher density points
        descDensityOrder = np.argsort(-densityComp)
        descDensityOrderIds = np.asarray(ids)[descDensityOrder]

        filterNum = int(self.topPercent * len(ids))
        filterIdx = descDensityOrderIds[:filterNum]
        filterIdx = list(map(int, filterIdx))


        # write to DB
        self.writeOutputIdsToDB(filterIdx, 'KdeFilter', {'topPer': self.topPercent})

        return filterIdx
